/*
Created by FINE
 */

#include "ns3/core-module.h"
#include "ns3/point-to-point-module.h"
#include "ns3/network-module.h"
#include "ns3/applications-module.h"
#include "ns3/wifi-module.h"
#include "ns3/mobility-module.h"
#include "ns3/csma-module.h"
#include "ns3/internet-module.h"
//NetAnim可视化工具的库
#include "ns3/netanim-module.h"



// Network Topology
//
// p2p 192.168.1.0/192.168.2.0
//  =======================                   APNODE
//  |         |           |                     *              STANODES
//  |         |           |     192.168.3.0     |           =============
// n1---------n2----------n3 .................. n4          n5          n6   
//                                  LAN         |           |           |    
//                                              ==========================
//                                                  wifi 192.168.4.0

using namespace ns3;

NS_LOG_COMPONENT_DEFINE ("Project2TopoTest");

int 
main (int argc, char *argv[])
{
//日志记录打开
  LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_INFO);
  LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_INFO);

//  CommandLine cmd;
//  cmd.AddValue ("ncsma", "Number of \"extra\" CSMA nodes/devices", ncsma);
//  cmd.AddValue ("nwifi", "Number of wifi STA devices", nwifi);
//用于处理命令行参数，解析用户通过命令行传递的参数，并将这些参数传递到程序中
//  cmd.Parse (argc,argv);

//LAN(CSMA)
//CSMA节点——>LAN
  NodeContainer csmaNodes;
  csmaNodes.Create (2);
//CSMA信道
  CsmaHelper csma;
  csma.SetChannelAttribute ("DataRate", StringValue ("100Mbps"));
  csma.SetChannelAttribute ("Delay", TimeValue (NanoSeconds (6560)));
/*
NS3 提供了各种时间单位来表示仿真中的时间：
其中包括 NanoSeconds（纳秒）
              Seconds（秒）
        MilliSeconds（毫秒） 
        MicroSeconds（微秒）
 */
//安装csma网络设备到csma节点
  NetDeviceContainer csmaDevices;
  csmaDevices = csma.Install(csmaNodes);


//PointToPoint
//创建P2P节点
  NodeContainer p2pNodes;
  p2pNodes.Add(csmaNodes.Get(0));
  p2pNodes.Create (2);
//创建、设置P2P信道参数
  PointToPointHelper pointToPoint;
  pointToPoint.SetDeviceAttribute ("DataRate", StringValue ("5Mbps"));
  pointToPoint.SetChannelAttribute ("Delay", StringValue ("2ms"));
//P2P网络设备，并安装到节点上(考虑后续设为p2pDevices.Get(0),p2pDevices.Get(1))
  NetDeviceContainer p2pDevice1,p2pDevice2;
  p2pDevice1 = pointToPoint.Install (p2pNodes.Get(0),p2pNodes.Get(1));
  p2pDevice2 = pointToPoint.Install (p2pNodes.Get(1),p2pNodes.Get(2));


//WLAN
//wifi普通节点与ap节点
  NodeContainer wifistaNodes;
  wifistaNodes.Create(1);
  wifistaNodes.Add(csmaNodes.Get(1));
  NodeContainer apNode;
  apNode.Create(1);
//wifi信道设置
  YansWifiChannelHelper wifichannel = YansWifiChannelHelper::Default ();
  YansWifiPhyHelper wifiphy = YansWifiPhyHelper::Default ();
  wifiphy.SetChannel (wifichannel.Create ());
//配置速率控制算法，AARF算法
  WifiHelper wifi;
  wifi.SetRemoteStationManager("ns3::AarfWifiManager");
//ST节点设置
  WifiMacHelper wifiMac;
  Ssid wifissid = Ssid("fby-wifi-ssid");
  wifiMac.SetType("ns3::StaWifiMac",
                  "Ssid", SsidValue(wifissid),
                  "ActiveProbing", BooleanValue(false));
//网络设备安装
  NetDeviceContainer wifiDevices;
  wifiDevices = wifi.Install(wifiphy, wifiMac, wifistaNodes);
//AP节点安装
//首先重新设置wifimac
  wifiMac.SetType ("ns3::ApWifiMac",
                   "Ssid", SsidValue (wifissid));
//安装AP网络设备
  NetDeviceContainer apDevices;
  apDevices = wifi.Install(wifiphy, wifiMac, apNode);


//移动模型
  MobilityHelper mobility;
//AP节点保持固定位置
  mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");
  mobility.Install(apNode);
//wifistanodes设置为：移动轨迹为线性远离AP，这里选择使用固定速度远离
  mobility.SetMobilityModel("ns3::ConstantVelocityMobilityModel");
  mobility.Install(wifistaNodes);


//协议栈安装
  InternetStackHelper stack;
  stack.Install(p2pNodes);
  stack.Install(wifistaNodes);
  stack.Install(apNode);
//注意这里p2pNodes不用安装该互联网协议栈

//IP地址分配
  Ipv4AddressHelper address;
//p2p网关
/*
在 NS3 中，将两条链路放在同一个子网下是不推荐的做法，尤其是当每条链路都有明确的物理分割时。
这样做会导致拓扑的不清晰，并且在实际网络中会引起路由问题，比如 ARP 广播混乱、MAC 层冲突等。

在标准网络设计中，点对点链路通常在不同的子网中进行划分。
如果强制将两条链路放在同一子网下，虽然可以编写脚本来实现，但这样会使网络结构在逻辑上不符合实际情况。

如果确实想将两条链路放在同一子网下，可以这样做：
将 Ipv4AddressHelper 设置相同的子网范围。
手动为每个节点分配 IP 地址，确保地址不冲突。

实现代码：
address.SetBase("192.168.1.0","255.255.255.0");
Ipv4InterfaceContainer p2pInterface1;
p2pInterface1 = address.Assign(p2pDevice1);
p2pInterface1.SetAddress(0,Ipv4Address("192.168.1.1"));
p2pInterface1.SetAddress(1,Ipv4Address("192.168.1.2"));
Ipv4InterfaceContainer p2pInterface2;
p2pInterface2 = address.Assign(p2pDevice2);
p2pInterface2.SetAddress(0,Ipv4Address("192.168.1.2"));
p2pInterface2.SetAddress(1,Ipv4Address("192.168.1.3"));

*/
//p2p子网1：192.168.1.0
  address.SetBase("192.168.1.0","255.255.255.0");
  Ipv4InterfaceContainer p2pInterface1;
  p2pInterface1 = address.Assign(p2pDevice1);
//p2p子网2：192.168.2.0
  address.SetBase("192.168.2.0","255.255.255.0");
  Ipv4InterfaceContainer p2pInterface2;
  p2pInterface2 = address.Assign(p2pDevice2);

//csma地址分配
  address.SetBase("192.168.3.0","255.255.255.0");
  Ipv4InterfaceContainer csmaInterfaces;
  csmaInterfaces = address.Assign(csmaDevices);

//wifi地址分配
  address.SetBase("192.168.4.0","255.255.255.0");
  Ipv4InterfaceContainer staInterfaces = address.Assign(wifiDevices);
  Ipv4InterfaceContainer apInterfaces = address.Assign(apDevices);


//udpechoSever/Client
  UdpEchoServerHelper udpechoServer(9);     //端口9

  ApplicationContainer udpserverApps;
  udpserverApps = udpechoServer.Install(apNode);
  udpserverApps.Start(Seconds(1.0));
  udpserverApps.Stop(Seconds(9.0));

  UdpEchoClientHelper udpechoClient(apInterfaces.GetAddress(0),9);    //(serverip,port)
  udpechoClient.SetAttribute ("MaxPackets", UintegerValue (1));
  udpechoClient.SetAttribute ("Interval", TimeValue (Seconds (1.0)));
  udpechoClient.SetAttribute ("PacketSize", UintegerValue (1024));
  ApplicationContainer udpclientApps = udpechoClient.Install(wifistaNodes);
  udpclientApps.Start(Seconds(2.0));
  udpclientApps.Stop(Seconds(9.0));

//动画跟踪文件
  AnimationInterface anim ("./XML/project2-animation.xml");  // XML 动画文件名

//全局路由打开
  Ipv4GlobalRoutingHelper::PopulateRoutingTables ();

  Simulator::Stop (Seconds (10.0));


  pointToPoint.EnablePcapAll ("Project2TopoTest");
  wifiphy.EnablePcap ("Project2TopoTest", apDevices.Get (0));
  csma.EnablePcap ("Project2TopoTest", csmaDevices.Get (0), true);

  Simulator::Run ();
  Simulator::Destroy ();
  return 0;
}

//./waf --run scratch/proj2 > ./LOGS/fby-proj2-log.log

//  NetAnim:
//  $ cd ns-allinone-3.27/natanim-3.108
//  $ ./NetAnim
//  open file.xml